Eddie's Lessons, version 3

Line Numbers

level: beginner

by Alyce Watson [http://alycesrestaurant.com/]

NL133 Home

::::::::::::::::::::::::::::::::::::::::::::::::::::

Chat Challenge

Eddie Version 3

Stylebits Corner

Progress Bars

Velleman Interface

Sprite Byte

Simulating BMP Buttons

Program Security

LB Functions

::::::::::::::::::::::::::::::::::::::::::::::::::::

Submission Guildlines

Newsletter Help

Index


What is Eddie?

Eddie is a new code editor written in Liberty BASIC, for Liberty BASIC. Eddie can be customized and extended to meet the needs of the individual programmer. The newest version is available here: Eddie!

Adding Line Numbers!

Version 3 of Eddie includes line numbers in the left margin. Have a look:

The line numbers are added with native Liberty BASIC commands, too! You need Liberty BASIC version 4.02 for this feature to work, because the ORIGIN? command is broken in version 4.01.

The origin?

In Liberty BASIC, when we discuss the origin, we don't mean, "The Origin of Species" by Charles Darwin. The ORIGIN is the upper left corner of a texteditor control. It also applies to the editor portion of a Text Window.

There are two versions of the ORIGIN command. One sets the origin, and the other returns the origin.

To set a certain row and column of text to appear at the upper left corner of the texteditor, use this command:

print #handle, "!origin column row" ;

This command forces the origin of the window to be row and column. This means that the row and column specified will appear in the upper left corner of the texteditor or text window.

"Row" refers to a line of text. "Column" refers to the characters in a line of text. Setting the origin effectively scrolls the text in the horizontal and vertical directions so that the line and character specified in the row and column designations appears at the upper left corner of the texteditor.

To implement line numbers in Eddie, we don't need to set the origin, we need to know the number of the row (line) of text appears at the origin. To accomplish that, we use:

 print #h, "!origin? columnVar rowVar" ;

This command causes the current text window origin to be returned. The origin is the upper left corner of the texteditor or textwindow. When a text window is first opened, the result would be row 1, column 1. The result is contained in the variables rowVar and columnVar.

In Eddie, we retrieve the number of the row that currently appears at the top of the texteditor and place it into a variable called row.

     #1.te "!origin? col row"

We'll need to know this row number when we update the line numbers in the margin.

Font Face

A code editor should use a mono-spaced font. This means that each character takes the same space (width). A variable-width font allows large characters, such as uppercase 'M' to take up more space than small characters, such as lowercase 'i'. Because it is easier to read code that lines up in columns, code editors use mono-spaced fonts such as Courier New. The line numbers in the margin also need to line up, so a mono-spaced font is right for that purpose as well.

Remember this important rule. When you give a FONT command in Liberty BASIC, the face name must be all one word. If you are using a font such as "Courier New", you must replace any blank spaces with underscore characters, like this: "Courier_New". Font facename designations are not case sensitive.

Font Size

To keep the line numbers in the margin in sync with the rows of text, we need to use fonts that are the same size in the texteditor and the graphicbox that serves as the line number margin. There are two ways to set the size of fonts in Liberty BASIC. We can set the size in pixels, or in points. Pixels are the discreet, smallest elements of the graphical screen. Points refer to the actual size that a font appears on the screen, as measured by a ruler. A 10 point font appears the same size on all screen resolutions. A font that is 10 pixels high appears quite large if you set your resolution to 640x480, but it looks much smaller if the same monitor is set to 1024x768.

The FONT command requires a font facename, and a size. Here is a font command that creates a font in the face, Arial, that is 10 points in size:

#handle "!font arial 10"

How does Liberty BASIC know that the "10" refers to points? It knows because there is only one number given in the size specification argument. If there are two numbers in that argument, Liberty BASIC sizes the font in pixels. To create an Arial font that is 16 pixels high:

#handle "!font arial 8 16"

There are two numbers in the size argument. The first refers to the width, the second to the height of the font in pixels. That command creates a font whose average character is 8 pixels in width and 16 pixels in height. To allow Windows to use the default height for a font, use "0" as the width specification:

#handle "!font arial 0 16"

Controls that can accept new strings of text require the exclamation point character to precede the FONT command, so that they know it is a command, not a new caption or string of text to display. A texteditor requires the '!' character in the command, while a graphicbox does not.

Eddie uses these font commands to set the font in the texteditor and in the graphicbox that serves as the line number margin:

    'set a custom font for the texteditor
    #1.te "!font courier_new 0 16"
    'set up the graphicbox
    #1.g "down; fill Buttonface; backcolor buttonface; flush"
    'use the same font as the texteditor
    #1.g "font courier_new 0 16"

Graphics Text

Be sure to read the [Graphics Text Tutorial in issue #108.]

The line number margin is actually just a graphicbox. The graphicbox is filled with the color, buttonface, so that it blends with the window background. "Buttonface" is not the same color on all systems. It is the color of the button and window background according to the current desktop color scheme. The buttonface color is also set to be the backcolor, which is the color that appears behind printed text.

Graphics text is placed in a graphicbox by sending a command that begins with the backslash character. (You can also use the pipes character.)

#handle "\Some text."

The backslash character forces a "carriage return/line feed" combination each time it is used.

#handle "\First line."
#handle "\Second line."

Graphics text appears at the current pen location. The LOWER LEFT corner of the text is used to position the text. Use the PLACE command to locate the text on the graphicbox. In Eddie, we position the first line of text two pixels from the left side, and 14 pixels down, remembering that the text is located with its lower left corner at the pen position.

     #1.g "place 2 14"

To create the row numbers in the graphicbox, we can simply print them in a loop:

     #1.g "place 2 14"

     for i = 1 to 100
        'using() aligns the numbers for us
         num$=using("####",i)
         #1.g "\";num$
     next

We used the handy command USING() to create a string that automatically aligns the numbers so that they look like this:

   6
   9
  10
  11
 101
 102
1000
1001

Without USING(), the line numbers would look like this:

6
9
10
11
101
102
1000
1001

Control Placement and Autoresizing

The line number margin is actually a graphicbox. The numbers look like they are printed right on the window. How does Eddie do that? The graphicbox border is removed with the STYLEBITS commmand. See Janet's article on using Stylebits here: [http://babek.info/libertybasicfiles/lbnews/nl130/stylebits01.htm] The border is removed by putting _WS_BORDER in the "removeBits" position of the STYLEBITS command, like this:

stylebits #1.g, 0,_WS_BORDER,0,0
graphicbox  #1.g, 0, 22, 39, 260

The graphicbox is placed on the left side of the window. The texteditor is positioned with its left side at the same location as the right side of the graphicbox, so they are directly next to one another. The graphicbox is placed at x=0 and has a width of 39. The texteditor is placed at x=39

graphicbox  #1.g, 0, 22, 39, 260
'a texteditor
texteditor  #1.te, 39, 22, 545, 280

Both controls were placed with their tops at y=22 to allow room for the button controls at the top of the window. The height of the texteditor is 20 pixels greater than the height of the graphicbox, though. If you look at a texteditor control, you'll see that the bottom portion actually holds the horizontal scrollbar.

Since no lines of text appear at that spot, there is no need for line numbers to appear at that level in the graphicbox, so the graphicbox is 20 pixels shorter than the texteditor. The red portion of this image shows the area where line numbers are not necessary.

The size of 20 pixels is an approximation of the size of a scrollbar. It might be a bit more or less than that on an individual's computer, depending on the desktop theme in use. It is not critical for our purposes to have the exact size of the scrollbar, so the 20 pixel difference is close enough.

The graphicbox and texteditor both get an AUTORESIZE command. This means that when the user resizes the window, these two controls resize properly as well, and we don't have to write code to accomplish this. Liberty BASCI does the calculations and resizes the controls for us.

    #1.g "autoresize"

    'cause the texteditor to resize automatically
    'when the user resizes the window
    #1.te "!autoresize"

The Timer

The timer is crucial to the line number feature in Eddie. We need to check the row at the origin periodically, to see if the line numbers in the margin need to be updated. We do this check using a TIMER. The timer statement looks like this:

    timer ms, [branchLabel]

The first argument is the number of milliseconds for the timer interval. There are 1000 milliseconds in one second. If the number "1000" were used, the timer would fire once per second. If the number was "500", the timer would fire once each half second. 250 milliseconds is a quarter second, and so on. The branch label specified in the timer statement is accessed each time the timer fires an event. The code in Eddie to activate the timer:

     TIMER 50, [activateTimer]

We'll want to turn the timer off when a long routine is executing in the program. We turn off the timer by setting the time interval to 0, and omitting the branch label designation:

    TIMER 0

There is only one timer available to a Liberty BASIC program. It can be turned on or off as many times as needed. The timer interval can be changed at any time as well.

Making it Work

Here's how Eddie implements scrolling line numbers in the left margin.

We've already discussed most of the routines to accomplish the line number scroll, but there are a few more to mention.

We know how to check for the row at the origin. If we store that value in a variable, the next time we check the origin, we can compare the two and see if there is a change. If the user hasn't scrolled the texteditor, there is no need to update the line numbers in the margin. If the row at the orgin has changed, we place that value into the "lastRow" variable, and go on with the line number update. Here's the code:

     #1.te "!origin? col row"
     'if it is the same as last check, do nothing
     if row = lastRow then wait
    'turn off timer for this routine
     TIMER 0
     'new lastRow is current row, for next check
     lastRow = row

We need to issue a FLUSH command to ensure that the graphic line numbers persist if the window is obscured. Each FLUSH consumes memory. We can release that memory with DELSEGMENT. If we use the optional ability to give a name to a drawing segment when we FLUSH, we can easily delete that segment when it is time to update the graphic line numbers. Here's how Eddie does it:

     #1.g "delsegment linenums"
     'code to update graphics
     'more code
     'more code
     'flush and give this segment the name *linenums*
     #1.g "flush linenums"

All that's left is to write the graphics text to print the row numbers. We can do that quite easily in a for/next loop. The counter variable is used in the printing of the line numbers. The counter variable starts with the value of the row at the origin, which is in the variable "row". We want the numbers to line up in columns, so we create a string with each number with the USING() function, as described above. Each new number in the loop is printed with a carriage return (the "\" character used for graphics text.) We start the printing at the current "row" value and print numbers in a loop until we get to the row value plus 100. It is unlikely that 100 lines will ever be displayed at one time in the texteditor visible portion, so we've used that number as our maximum. Remember that the graphicbox is sized so that it is only visible in the text portion of the texteditor, so excess line numbers that might be printed are hidden from view.

     'use top row number as starting number for line numbers
     for i = row to row+100
        'using() aligns the numbers for us
         num$=using("####",i)
         #1.g "\";num$
     next

Here is the entire code routine that is activated by the timer to print and synchronize line numbers in the left margin with the lines in the texteditor.

[activateTimer]
    'check the number of the row at the top of the texteditor
     #1.te "!origin? col row"
     'if it is the same as last check, do nothing
     if row = lastRow then wait
    'turn off timer for this routine
     TIMER 0
     'new lastRow is current row, for next check
     lastRow = row
     'delete previously named drawing segment called *linenums*
     #1.g "delsegment linenums"
     'location to start drawing line numbers
     #1.g "place 2 14"
     'fill with default buttonface color to erase previous line numbers
     #1.g "fill buttonface"
     'use top row number as starting number for line numbers
     for i = row to row+100
        'using() aligns the numbers for us
         num$=using("####",i)
         #1.g "\";num$
     next
     'flush and give this segment the name *linenums*
     #1.g "flush linenums"
     'start timer again
     TIMER 50, [activateTimer]
     wait

Eddie's Code

Eddie's code for version 3 can be found here: Eddie's code

Feel free to modify Eddie for your own use! Have fun!


NL133 Home

::::::::::::::::::::::::::::::::::::::::::::::::::::

Chat Challenge

Eddie Version 3

Stylebits Corner

Progress Bars

Velleman Interface

Sprite Byte

Simulating BMP Buttons

Program Security

LB Functions

::::::::::::::::::::::::::::::::::::::::::::::::::::

Submission Guildlines

Newsletter Help

Index